package logic

import (
	"context"
	"errors"
	"fmt"
	"gozero-microservices/gateway-api/internal/svc"
	"gozero-microservices/gateway-api/internal/types"
	"gozero-microservices/gateway-api/utils"
	"gozero-microservices/inventory-service/rpc/types/inventory"
	"gozero-microservices/order-service/rpc/types/order"

	"github.com/dtm-labs/client/dtmcli"
	"github.com/dtm-labs/client/dtmgrpc"
	"github.com/dtm-labs/client/dtmgrpc/dtmgimp"
	"github.com/dtm-labs/client/workflow"
	"github.com/zeromicro/go-zero/core/logx"
	"google.golang.org/protobuf/proto"
)

/*
 +----------------------------------------------------------------------
 + Title        : BannerLogic
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2025-08-08
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : 横幅逻辑
 +----------------------------------------------------------------------
*/

type OrderLogic struct {
	BaseLogic
}

var (
	workflowRegisterCount = 1
)

/**
 * 创建订单逻辑实例
 *
 * @param context.Context ctx ----------------------必传
 * @param *svc.ServiceContext svcCtx ---------------必传
 * @return *OrderLogic
 * @author huwl
 * @date 2025/10/26 12:54:03
 */
func NewOrderLogicInstance(ctx context.Context, svcCtx *svc.ServiceContext) *OrderLogic {
	return &OrderLogic{
		BaseLogic{
			Ctx:    ctx,
			SvcCtx: svcCtx,
			Logger: logx.WithContext(ctx),
		},
	}
}

/**
 * 创建订单
 *
 * @param types.OrderCreateRequest params ----------必传
 * @return uint64, error
 * @author huwl
 * @date 2025/10/24 15:29:35
 */
func (logic *OrderLogic) GenerateOrder(params types.OrderCreateRequest) (uint64, error) {
	if params.GoodsSkuId <= 0 {
		return 0, errors.New("请选择商品规格选项")
	}
	if params.Platform <= 0 {
		return 0, errors.New("商城平台错误")
	}
	if params.Username == "" {
		return 0, errors.New("用户名不能为空")
	}
	if params.AddressId <= 0 {
		return 0, errors.New("收货地址Id错误")
	}
	if params.Num <= 0 {
		return 0, errors.New("请选择商品数量")
	}

	// 获取dtm grpc服务地址
	dtmGrpcServerUrl := ""
	switch logic.SvcCtx.Config.Env {
	case "dev":
		dtmGrpcServerUrl = logic.SvcCtx.Config.DtmConf.Dev.GrpcServer
	case "test":
		dtmGrpcServerUrl = logic.SvcCtx.Config.DtmConf.Test.GrpcServer
	case "prod":
		dtmGrpcServerUrl = logic.SvcCtx.Config.DtmConf.Prod.GrpcServer
	}

	gid := dtmgrpc.MustGenGid(dtmGrpcServerUrl)

	// 组装下单参数
	createOrderParams := &order.CreateOrderRequest{
		Platform:   uint32(params.Platform),
		Uid:        2213,
		Username:   params.Username,
		OrderMoney: 3400,
		PayMoney:   3400,
		Goods: []*order.Goods{
			&order.Goods{
				GoodsSkuId:    uint32(params.GoodsSkuId),
				GoodsId:       25,
				GoodsName:     "肉色0d丝袜超薄款性感女隐形全透明薄款脚尖无痕",
				GoodsNum:      uint32(params.Num),
				GoodsCoverUrl: "trident/static/upload_images/2022-09-06/cd4342bc6d28864fffa5d66498c38f1a.jpg",
				GoodsPrice:    1600,
				PayMoney:      1600,
				GoodsSkuText:  "浅肤色 均码",
			},
		},
		DeliveryAddress: &order.DeliveryAddress{
			Name:       "sdfasd",
			Mobile:     "13130244248",
			ProvinceId: 2,
			CityId:     52,
			DistrictId: 321,
			Region:     "xxxx-xx-xx",
			Address:    "xxxxxxxx",
		},
		Barrier: &order.Barrier{
			TransType: "workflow",
			Gid:       gid,
			BranchId:  "02",
		},
	}

	// 组装请求参数
	inventories := []*order.Inventory{
		&order.Inventory{
			Id:           1179,
			InventoryNum: 1,
		},
	}

	// 使用saga模式,如果需要获取订单编号则存在问题
	// 由于执行事务分支是异步延时的所有即使将订单编号写入reids,下面获取的时候需要睡眠
	// 主要的是睡眠时间无法控制
	// // 获取订单服务地址【使用sga、tcc的时候需要】
	// orderRpcServer, err := logic.SvcCtx.Config.OrderRpc.BuildTarget()
	// if err != nil {
	// 	return 0, err
	// }

	// // 获取库存服务地址【使用sga、tcc的时候需要】
	// inventoryRpcServer, err := logic.SvcCtx.Config.InventoryRpc.BuildTarget()
	// if err != nil {
	// 	return 0, err
	// }
	// saga := dtmgrpc.NewSagaGrpc(dtmGrpcServerUrl, gid).
	// 	// Add(inventoryRpcServer+"/inventoryService.InventorySrv/DeductInventory", inventoryRpcServer+"/inventoryService.InventorySrv/RevivificationInventory", inventoryParams).
	// 	// Add(orderRpcServer+"/orderService.OrderSrv/CreateOrder", orderRpcServer+"/orderService.OrderSrv/DestroyOrder", createOrderParams)
	// 	Add("127.0.0.1:9514/inventoryService.InventorySrv/DeductInventory", "127.0.0.1:9514/inventoryService.InventorySrv/RevivificationInventory", inventoryParams).
	// 	Add("127.0.0.1:9512/orderService.OrderSrv/CreateOrder", "127.0.0.1:9512/orderService.OrderSrv/DestroyOrder", createOrderParams)

	// err := saga.Submit()
	// if err != nil {
	// 	return 0, errors.New("submit data to  dtm-server err  : " + err.Error())
	// }

	// // 从redis中获取订单编号
	// ri, err := tools.NewRedisInstance(logic.SvcCtx)
	// if err != nil {
	// 	return 0, err
	// }

	// time.Sleep(time.Second * 1)
	// oid, err := ri.RedisGet("oid:" + gid)
	// if err != nil {
	// 	return 0, err
	// }

	// orderId, _ := strconv.Atoi(oid)
	// return uint64(orderId), nil

	// 事务逻辑线：扣除库存 ---> 成功 ---> 创建订单 ---> 成功 ---> 事务结束
	// 		执行结果：扣除库存、创建订单【成功】
	// 事务逻辑线：扣除库存 ---> 成功 ---> 创建订单 ---> 失败 ---> 还原库存 ---> 成功 ---> 事务结束
	// 		执行结果：扣除库存【成功】、创建订单【失败】、还原库存【成功】
	// 事务逻辑线：扣除库存 ---> 成功 ---> 创建订单 ---> 失败 ---> 还原库存 ---> 失败 ---> ?
	// 		执行结果：扣除库存【成功】、创建订单【失败：参数错误】、还原库存【参数错误导致的失败】
	// 				(参数、执行数据库)错误：还原库存失败会导致一直重试，如果确实不行。
	// 							 解决方式：强制关闭dtm事务，1.人工干预，2.将数据加入消息中间件中重试
	// 事务逻辑线：扣除库存 ---> 失败 ---> ?
	// 		执行结果：扣除库存【失败】、还原库存【成功】
	// 		解决方式：未扣除库存则不应该还原，因此需要在还原库存的时候判断是否已经扣除

	var oid uint64
	// 使用DTM分布式事务
	wfName := "create_order_workflow"
	if workflowRegisterCount == 1 {
		err := workflow.Register(wfName, func(wf *workflow.Workflow, data []byte) error {
			// 解析请求参数
			workflowRequest := &order.WorkflowRequest{}
			if err := proto.Unmarshal(data, workflowRequest); err != nil {
				return err
			}

			// 重新组装库存参数
			inventories := []*inventory.Inventory{}
			for _, v := range workflowRequest.Inventories {
				inventories = append(inventories, &inventory.Inventory{
					Id:           v.Id,
					InventoryNum: v.InventoryNum,
				})
			}
			inventoryRequest := &inventory.InventoryRequest{
				Inventories: inventories,
			}

			// 分支1：还原库存
			wf.NewBranch().OnRollback(func(bb *dtmcli.BranchBarrier) error {
				// 组装屏蔽参数
				inventoryRequest.Barrier = &inventory.Barrier{
					TransType: workflowRequest.Barrier.TransType,
					Gid:       workflowRequest.Barrier.Gid,
					BranchId:  "01",
				}
				_, err := logic.SvcCtx.InventorySrv.RevivificationInventory(wf.Context, inventoryRequest)
				return err
			})

			// 扣减库存
			// 组装屏蔽参数
			inventoryRequest.Barrier = &inventory.Barrier{
				TransType: workflowRequest.Barrier.TransType,
				Gid:       workflowRequest.Barrier.Gid,
				BranchId:  "00",
			}
			_, err := logic.SvcCtx.InventorySrv.DeductInventory(wf.Context, inventoryRequest)
			if err != nil {
				return err
			}
			fmt.Println("err ========= ", err)

			// 分支2：创建订单
			res, err := logic.SvcCtx.OrderSrv.CreateOrder(wf.Context, workflowRequest.CreateOrderRequest)
			if err != nil {
				return err
			}
			fmt.Println("err ========= ", err)

			resData, err := utils.ParseAnyMap(res.Data)
			if err != nil {
				return err
			}

			oid = uint64(resData["oid"].(int64))

			return nil
		})

		if err != nil {
			return 0, err
		}

		workflowRegisterCount++
	}

	// 组装数据到DTM Workflow中使用
	wr := order.WorkflowRequest{
		Inventories:        inventories,
		CreateOrderRequest: createOrderParams,
		Barrier: &order.Barrier{
			TransType: "workflow",
			Gid:       gid,
		},
	}

	// DTM 执行Workflow事务
	err := workflow.Execute(
		wfName,
		gid,
		dtmgimp.MustProtoMarshal(&wr),
	)
	if err != nil {
		return 0, err
	}

	return oid, nil
}

/**
 * 删除
 *
 * @param string oids -------------------------必传,订单编号
 * @return error
 * @author huwl
 * @date 2025/10/26 17:30:12
 */
func (logic *OrderLogic) DeleteOrder(oids string) error {
	if oids == "" {
		return errors.New("订单编号错误")
	}

	// 远程调用订单服务之订单
	_, err := logic.SvcCtx.OrderSrv.DeleteOrder(logic.Ctx, &order.DeleteRequest{
		Ids: oids,
	})
	if err != nil {
		return err
	}

	return nil
}

/**
 * 列表
 *
 * @param uint64 Oid --------------------------非必传,订单Id
 * @param string Oids -------------------------非必传,订单Id支持多个
 * @param uint8 Platform ----------------------非必传,商城平台
 * @param uint64 Uid --------------------------非必传,用户Id
 * @param string Uids -------------------------非必传,用户Id支持多个
 * @param string Username ---------------------非必传,用户名
 * @param string GoodsName --------------------非必传,商品名称
 * @param uint8 PayChannel --------------------非必传,支付渠道
 * @param string IsDelete ---------------------非必传,是否删除
 * @param string Status -----------------------非必传,状态支持多个：1,-1
 * @param string StartCreateTime --------------非必传,开始创建时间
 * @param string EndCreateTime ----------------非必传,结束创建时间
 * @param string SortField --------------------非必传,排序字段
 * @param string SortType ---------------------非必传,排序方式
 * @param uint8 Page --------------------------非必传,页码
 * @param int8 PageSize -----------------------非必传,条数
 * @return *order.OrderListResult, error
 * @author huwl
 */
func (logic *OrderLogic) OrderList(params types.QueryOrderRequest) (*order.OrderListResult, error) {
	// 请求状态说明【参数status】
	// "all"	=> 全部
	// "1"		=> 待付款 --------- 对应数据库：0
	// "2"		=> 待发货 --------- 对应数据库：1,2
	// "3"		=> 待收货 --------- 对应数据库：3,4,5
	// "4"		=> 待评价 --------- 对应数据库：6
	// "5"		=> 退款/退货 ------ 对应数据库：-3,-2,8
	// "6"		=> 已关闭 --------- 对应数据库：-5
	// "7"		=> 已取消 --------- 对应数据库：-1
	// "8"		=> 已完成 --------- 对应数据库：7
	// "9"		=> 退款中 --------- 对应数据库：8
	// "10"		=> 已退款 --------- 对应数据库：-2

	// 数据库状态说明
	// 订单状态：-5-已关闭，-3-已部分退款，-2-已退款，-1-已取消，
	// 0-待付款，1-已付款，2-待发货，3-已发货，4-已揽件，5-派件中，6-待评价，7-已完成，8-退款中
	isDelete := params.IsDelete
	if isDelete == "" {
		isDelete = "all"
	}

	state := "all"
	switch params.Status {
	case "1":
		state = "0"
	case "2":
		state = "1,2"
	case "3":
		state = "3,4,5"
	case "4":
		state = "6"
	case "5":
		state = "-3,-2,8"
	case "6":
		state = "-5"
	case "7":
		state = "-1"
	case "8":
		state = "7"
	case "9":
		state = "8"
	case "10":
		state = "-2"
	}

	// 远程调用订单服务之订单
	return logic.SvcCtx.OrderSrv.OrderList(logic.Ctx, &order.QueryOrderRequest{
		Oid:             params.Oid,
		Oids:            params.Oids,
		Platform:        uint32(params.Platform),
		Uid:             params.Uid,
		Uids:            params.Uids,
		Username:        params.Username,
		GoodsName:       params.GoodsName,
		PayChannel:      uint32(params.PayChannel),
		IsDelete:        isDelete,
		Status:          state,
		StartCreateTime: params.StartCreateTime,
		EndCreateTime:   params.EndCreateTime,
		SortField:       params.SortField,
		SortType:        params.SortType,
		Page:            uint32(params.Page),
		PageSize:        int32(params.PageSize),
	})
}
